package org.example.lotterysystem.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import org.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.example.lotterysystem.common.exception.ServiceException;
import org.example.lotterysystem.common.utils.JWTUtil;
import org.example.lotterysystem.common.utils.RedisUtil;
import org.example.lotterysystem.common.utils.RegexUtil;
import org.example.lotterysystem.controller.param.UserLoginParam;
import org.example.lotterysystem.controller.param.UserRegisterParam;
import org.example.lotterysystem.controller.param.ShortMessageLoginParam;
import org.example.lotterysystem.controller.param.UserPasswordLoginParam;
import org.example.lotterysystem.dao.dataobject.Encrypt;
import org.example.lotterysystem.dao.dataobject.UserDO;
import org.example.lotterysystem.dao.mapper.UserMapper;
import org.example.lotterysystem.service.UserService;
import org.example.lotterysystem.service.VerificationCodeService;
import org.example.lotterysystem.service.dto.UserDTO;
import org.example.lotterysystem.service.dto.UserLoginDTO;
import org.example.lotterysystem.service.dto.UserRegisterDTO;
import org.example.lotterysystem.service.dto.UserSendCodeDTO;
import org.example.lotterysystem.service.enmus.UserIdentityEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private VerificationCodeService verificationCodeService;

    /**
     * 注册
     * @param param
     * @return
     */
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {

        //下面开始校验注册信息
        checkRegisterInfo(param);

        //进行加密
        UserDO userDO=new UserDO();
        userDO.setUserName(param.getName());
        userDO.setIdentity(param.getIdentity());
        userDO.setEmail(param.getEmail());
        if (StringUtils.hasText(param.getPassword())){
            userDO.setPassword(DigestUtil.sha256Hex(param.getPassword()));
        }
        userDO.setPhoneNumber(new Encrypt(param.getPhoneNumber()));
        //写入数据库
        userMapper.insert(userDO);
        //构造返回信息
        UserRegisterDTO userRegisterDTO=new UserRegisterDTO();
        userRegisterDTO.setId(userDO.getId());
        //返回结果
        return userRegisterDTO;
    }

    /**
     * 登录
     * @param param
     * @return
     */
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        //下面这种写法,目的是为了,区分两种身份,一个是管理员,一个是非管理员
        UserLoginDTO userLoginDTO;
        if(param instanceof UserPasswordLoginParam loginParam){
            //密码登录
            userLoginDTO=loginByUserPassword(loginParam);
        } else if (param instanceof ShortMessageLoginParam loginParam) {
            userLoginDTO=loginByShortMessage(loginParam);
        }else{
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXIST);
        }
        return userLoginDTO;
    }

    /**
     * 发送验证码
     * @param phoneNumber
     * @return
     */
    @Override
    public UserSendCodeDTO sendCode(String phoneNumber) {
        if(RegexUtil.checkMobile(phoneNumber)){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_FORMAT_ERROR);
        }
        //发送验证码,并将验证码存到redis中
        verificationCodeService.sendVerificationCode(phoneNumber);
        String verificationCode = verificationCodeService.getVerificationCode(phoneNumber);
        if(verificationCode==null){
            throw new ServiceException(ServiceErrorCodeConstants.Code_NOT_SEND);
        }
        //构造返回数据
        UserSendCodeDTO userSendCodeDTO=new UserSendCodeDTO();
        userSendCodeDTO.setCode(verificationCode);
        return userSendCodeDTO;
    }

    @Override
    public List<UserDTO> findUserInfo(UserIdentityEnum userIdentityEnum) {
        //判断用户的身份,如果为空,则返回null,不为空则返回对用的身份
        String identityString=null==userIdentityEnum?null:userIdentityEnum.name();
        //向数据库查询数据
        List<UserDO> userDOS = userMapper.selectUserListByIdentity(identityString);
        //将查询的数据进行转换

        //对下面代码进行解释,首先userDOS.stream()这个方法就是转换成流可以对内部元素进行操作
        //然后对流中的每个对象进行一一映射,拿到每个对象进行后续操作,类似一个循环,当每个对象都进行下面的操作后
        //执行collect(Collectors.toList());这个方法,就是将这些对象放到一个新的列表中,用userDTOList来接收;
        List<UserDTO> userDTOList=userDOS.stream().map(userDO -> {
            UserDTO userDTO=new UserDTO();
            userDTO.setUserId(userDO.getId());
            userDTO.setUserName(userDO.getUserName());
            userDTO.setEmail(userDO.getEmail());
            userDTO.setPhoneNumber(userDO.getPhoneNumber().getValue());
            userDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
            return userDTO;
        }).collect(Collectors.toList());

        return userDTOList;
    }


    /**
     * 通过短信验证码登录
     * @param loginParam
     * @return
     */
    private UserLoginDTO loginByShortMessage(ShortMessageLoginParam loginParam) {
        //验证码登录只要判断手机号即可,即只要校验手机号
        if(!RegexUtil.checkMobile(loginParam.getLoginMobile())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_FORMAT_ERROR);
        }
        //通过数据库查询数据
        UserDO userDO = userMapper.selectPasswordByPhoneNumber(new Encrypt(loginParam.getLoginMobile()));
        if(userDO==null){
            throw new ServiceException(ServiceErrorCodeConstants.USER_LOGIN_NOT_EXISTS);
        } else if (StringUtils.hasText(loginParam.getMandatoryIdentity())&&!loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        //校验验证码
        //获取验证码
        String verificationCode = verificationCodeService.getVerificationCode(loginParam.getLoginMobile());
        if(!verificationCode.equals(loginParam.getVerificationCode())){
            throw new ServiceException(ServiceErrorCodeConstants.ERROR_CODE);
        }

        //构造返回数据
        UserLoginDTO userLoginDTO=new UserLoginDTO();
        Map<String,Object> claim=new HashMap<>();
        claim.put("id",userDO.getId());
        claim.put("identity",userDO.getIdentity());
        String token = JWTUtil.genJwt(claim);
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

    /**
     * 通过手机号密码登录
     * @param loginParam
     * @return
     */
    private UserLoginDTO loginByUserPassword(UserPasswordLoginParam loginParam) {
        UserDO userDO=null;
        //判断登录方式
        if(RegexUtil.checkMail(loginParam.getLoginName())){
            //邮箱登录
            //通过邮箱查询密码
            String password = userMapper.selectPasswordByMail(loginParam.getLoginName());


        }else if(RegexUtil.checkMobile(loginParam.getLoginName())){
            //电话号码登录
            //通过电话号码查询密码
            userDO = userMapper.selectPasswordByPhoneNumber(new Encrypt(loginParam.getLoginName()));
        }else{
            throw new ServiceException(ServiceErrorCodeConstants.USER_LOGIN_NOT_EXISTS);
        }
        //对从数据库拿到的值进行校验
        if(userDO==null){
            throw new ServiceException(ServiceErrorCodeConstants.USER_MSG_IS_EMPTY);
        } else if (StringUtils.hasText(loginParam.getMandatoryIdentity())&&
                !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            //走到这里,说明用户身份为管理员,要强制身份登录.
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        } else if (!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDO.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }
        //插入返回值(JWT)
        Map<String,Object> claim=new HashMap<>();
        claim.put("id",userDO.getId());
        claim.put("identity",userDO.getIdentity());
        String token = JWTUtil.genJwt(claim);
        UserLoginDTO userLoginDTO=new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;

    }

    /**
     * 校验这侧填写的信息
     * @param param
     */
    private void checkRegisterInfo(UserRegisterParam param) {
        //首先判断param是否为空
        if(null==param){
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_EMPTY);
        }

        //校验邮箱格式
        if(!RegexUtil.checkMail(param.getEmail())){
            throw new ServiceException(ServiceErrorCodeConstants.EMAIL_FORMAT_ERROR);
        }
        //校验密码格式
        if(StringUtils.hasText(param.getPassword())&&!RegexUtil.checkPassword(param.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_FORMAT_ERROR);
        }
        //校验身份信息
        if(null== UserIdentityEnum.forName(param.getIdentity())){
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_IS_EMPTY);
        }
        //校验手机号格式
        if(!RegexUtil.checkMobile(param.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_FORMAT_ERROR);
        }
        //校验管理员密码必填
        if(param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                &&!StringUtils.hasText(param.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
        }
        //校验邮箱是否被使用,就是从数据库中查询
        if(checkMailUsed(param.getEmail())){
            throw new ServiceException(ServiceErrorCodeConstants.EMAIL_USED);
        }
        //校验手机号是否被使用
        if(checkPhoneNumberUsed(param.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }

    }

    /**
     *
     * 校验邮箱是否已经被使用
     * @param email
     * @return
     */
    private boolean checkMailUsed(String email){
        int i = userMapper.countByEmail(email);

        return i>0;
    }

    /**
     * 校验手机号是否已经被使用
     * @param phoneNumber
     * @return
     */
    private boolean checkPhoneNumberUsed(String phoneNumber){
        int i = userMapper.countByPhoneNumber(new Encrypt(phoneNumber));
        return i>0;
    }
}
